const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败

class MyPromise {
  static all (arr) {
    const results = []
    const len = arr.length
    let index = 0
    return new MyPromise((resolve, reject) => {
      const addData = (key, val) => {
        results[key] = val
        index += 1
        if (index === len) {
          resolve(results)
        }
      }
      for (let i = 0; i < len; i += 1) {
        if (arr[i] instanceof MyPromise) {
          arr[i].then(data => {
            addData(i, data)
          }, err => {
            reject(err)
          })
        } else {
          addData(i, arr[i])
        }
      }
    })
  }

  static race (arr) {
    let result = undefined
    const len = arr.length
    return new MyPromise((resolve, reject) => {
      for (let i = 0; i < len; i += 1) {
        if (arr[i] instanceof MyPromise) {
          arr[i].then(data => {
            resolve(data)
          }, err => {
            reject(err)
          })
        } else {
          resolve(arr[i])
        }
      }
    })
  }

  static resolve (val) {
    if (val instanceof MyPromise) return val
    return new MyPromise((resolve) => resolve(val))
  }

  constructor (executor) {
    try {
      executor(this.resolve, this.reject)
    } catch (error) {
      this.reject(error)
    }
  }
  // promise状态
  status = PENDING
  // 成功返回值
  value = undefined
  // 失败返回值
  err = undefined
  // 成功回调
  successCallback = []
  // 失败回调
  failCallback = []

  resolve = value => {
    // 状态不是pending时停止执行
    if (this.status !== PENDING) return
    this.status = FULFILLED
    this.value = value
    while (this.successCallback.length) {
      this.successCallback.shift()()
    }
  }

  reject = err => {
    // 状态不是pending时停止执行
    if (this.status !== PENDING) return
    this.status = REJECTED
    this.err = err
    while (this.failCallback.length) {
      this.failCallback.shift()()
    }
  }

  then (successCallback, failCallback) {
    successCallback = successCallback || (value => value)
    failCallback = failCallback || (err => { throw err })
    const promise = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            const x = successCallback(this.value)
            /* 
              判断x的值是普通值还是promise对象
              普通值直接调用resolve
              promise对象根据返回结果决定调用resolve还是reject
            */
            resolvePromise(promise, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0)
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            const x = failCallback(this.err)
            resolvePromise(promise, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0)
      } else {
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              const x = successCallback(this.value)
              /* 
                判断x的值是普通值还是promise对象
                普通值直接调用resolve
                promise对象根据返回结果决定调用resolve还是reject
              */
              resolvePromise(promise, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          }, 0)
        })
        this.failCallback.push(() => {
          setTimeout(() => {
            try {
              const x = failCallback(this.err)
              resolvePromise(promise, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          }, 0)
        })
      }
    })
    return promise
  }

  finally (callback) {
    return this.then(data => {
      return MyPromise.resolve(callback()).then(() => data)
    }, err => {
      return MyPromise.resolve(callback()).then(() => { throw err })
    })
  }

  catch (failCallback) {
    return this.then(undefined, failCallback)
  }
}

function resolvePromise (promise, x, resolve, reject) {
  if (promise === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  if (x instanceof MyPromise) {
    x.then(data => {
      resolve(data)
    }, err => {
      reject(err)
    })
  } else {
    resolve(x)
  }
}

export default MyPromise
// module.exports = MyPromise


